package com.diandian.backend.dingding.data.service;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.diandian.base.boot.starter.exception.DefaultException;
import com.diandian.base.boot.starter.response.PageResult;
import com.diandian.backend.dingding.data.domain.DingdingStaffSalary;
import com.diandian.backend.dingding.data.dto.*;
import com.diandian.backend.dingding.data.mapper.DingdingStaffSalaryMapper;
import com.diandian.backend.dingding.data.resp.DingdingStaffSalaryDetailResp;
import com.diandian.backend.dingding.data.resp.DingdingStaffSalaryPageResp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.diandian.backend.dingding.data.resp.DingdingStaffSalaryListResp;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import com.google.common.collect.Lists;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

@Service
public class DingdingStaffSalaryService {

    private static final Logger logger = LoggerFactory.getLogger(DingdingStaffSalaryService.class);

    @Autowired
    private DingdingStaffSalaryMapper dingdingStaffSalaryMapper;

    private static  int IMPORT_TOTAL_COUNT = 0; // 导入总数量
    private static int IMPORT_SUCCESS_COUNT = 0; // 执行导入数量
    private static int IMPORT_EXECUTE_COUNT = 0; // 执行次数

    public PageResult page(DingdingStaffSalaryPageParamDTO requestDTO){

        requestDTO.check();
        int pageNum = requestDTO.getPageNum();
        int pageSize = requestDTO.getPageSize();

        // 查询总数
        int totalCount = dingdingStaffSalaryMapper.getCount(requestDTO);

        // 查询数据集合
        List<DingdingStaffSalary> dataList = dingdingStaffSalaryMapper.getPage(requestDTO, requestDTO.getPageSize(), (requestDTO.getPageNum() - 1) * requestDTO.getPageSize());

        // 组装数据
        List<DingdingStaffSalaryPageResp> respList = new ArrayList<>();
        if(dataList != null && dataList.size() > 0){
            dataList.stream().forEach( x ->{
                DingdingStaffSalaryPageResp item = new DingdingStaffSalaryPageResp();
                item.setId(x.getId());
                item.setBelong(x.getBelong());
                item.setCreateUserId(x.getCreateUserId());
                item.setCreateUserName(x.getCreateUserName());
                item.setCreateTime(x.getCreateTime());
                item.setOrgId(x.getOrgId());
                item.setOrgCode(x.getOrgCode());
                item.setUpdateUserId(x.getUpdateUserId());
                item.setUpdateUserName(x.getUpdateUserName());
                item.setUpdateTime(x.getUpdateTime());
                item.setDel(x.getDel());
                item.setUserId(x.getUserId());
                item.setPostWage(x.getPostWage());
                item.setBasicSalary(x.getBasicSalary());
                item.setMonthPerformanceSalaryStandard(x.getMonthPerformanceSalaryStandard());
                item.setMonthPerformanceScore(x.getMonthPerformanceScore());
                item.setMonthPerformanceCoefficient(x.getMonthPerformanceCoefficient());
                item.setMonthPerformanceSalary(x.getMonthPerformanceSalary());
                item.setOtherSalary(x.getOtherSalary());
                item.setBeforeTaxDeductionTotal(x.getBeforeTaxDeductionTotal());
                item.setSalariesPayable(x.getSalariesPayable());
                item.setRewardXinghuoJlb(x.getRewardXinghuoJlb());
                item.setRewardXinghuoMzc(x.getRewardXinghuoMzc());
                item.setRewardXinghuoYl(x.getRewardXinghuoYl());
                item.setRewardXinghuoFh(x.getRewardXinghuoFh());
                item.setRewardXinghuoHpnk(x.getRewardXinghuoHpnk());
                item.setRewardXinghuoBym(x.getRewardXinghuoBym());
                item.setRewardXinghuoDz(x.getRewardXinghuoDz());
                item.setRewardXinghuoXk(x.getRewardXinghuoXk());
                item.setRewardXinghuoFnzt(x.getRewardXinghuoFnzt());
                item.setRewardXinghuoYypzt(x.getRewardXinghuoYypzt());
                item.setRewardXinghuoXhlyxm(x.getRewardXinghuoXhlyxm());
                item.setRewardPkNf(x.getRewardPkNf());
                item.setRewardPkRy(x.getRewardPkRy());
                item.setRewardPkSp(x.getRewardPkSp());
                item.setRewardPkYyp(x.getRewardPkYyp());
                item.setRewardPkFn(x.getRewardPkFn());
                item.setRewardPkHj(x.getRewardPkHj());
                item.setDividend(x.getDividend());
                item.setRewardTotal(x.getRewardTotal());
                item.setSalaryTotal(x.getSalaryTotal());
                item.setSalarySchedule(x.getSalarySchedule());
             respList.add(item);
            });
        }

        return new PageResult(pageNum, pageSize, totalCount, respList);
    }

    public List<DingdingStaffSalaryListResp> getList(DingdingStaffSalaryListParamDTO requestDTO){

        // 查询数据集合
        List<DingdingStaffSalary> dataList = dingdingStaffSalaryMapper.getList(requestDTO);

        // 组装数据
        List<DingdingStaffSalaryListResp> respList = new ArrayList<>();
        if(dataList != null && dataList.size() > 0){
            dataList.stream().forEach( x ->{
                DingdingStaffSalaryListResp item = new DingdingStaffSalaryListResp();
                item.setId(x.getId());
                item.setBelong(x.getBelong());
                item.setCreateUserId(x.getCreateUserId());
                item.setCreateUserName(x.getCreateUserName());
                item.setCreateTime(x.getCreateTime());
                item.setOrgId(x.getOrgId());
                item.setOrgCode(x.getOrgCode());
                item.setUpdateUserId(x.getUpdateUserId());
                item.setUpdateUserName(x.getUpdateUserName());
                item.setUpdateTime(x.getUpdateTime());
                item.setDel(x.getDel());
                item.setUserId(x.getUserId());
                item.setPostWage(x.getPostWage());
                item.setBasicSalary(x.getBasicSalary());
                item.setMonthPerformanceSalaryStandard(x.getMonthPerformanceSalaryStandard());
                item.setMonthPerformanceScore(x.getMonthPerformanceScore());
                item.setMonthPerformanceCoefficient(x.getMonthPerformanceCoefficient());
                item.setMonthPerformanceSalary(x.getMonthPerformanceSalary());
                item.setOtherSalary(x.getOtherSalary());
                item.setBeforeTaxDeductionTotal(x.getBeforeTaxDeductionTotal());
                item.setSalariesPayable(x.getSalariesPayable());
                item.setRewardXinghuoJlb(x.getRewardXinghuoJlb());
                item.setRewardXinghuoMzc(x.getRewardXinghuoMzc());
                item.setRewardXinghuoYl(x.getRewardXinghuoYl());
                item.setRewardXinghuoFh(x.getRewardXinghuoFh());
                item.setRewardXinghuoHpnk(x.getRewardXinghuoHpnk());
                item.setRewardXinghuoBym(x.getRewardXinghuoBym());
                item.setRewardXinghuoDz(x.getRewardXinghuoDz());
                item.setRewardXinghuoXk(x.getRewardXinghuoXk());
                item.setRewardXinghuoFnzt(x.getRewardXinghuoFnzt());
                item.setRewardXinghuoYypzt(x.getRewardXinghuoYypzt());
                item.setRewardXinghuoXhlyxm(x.getRewardXinghuoXhlyxm());
                item.setRewardPkNf(x.getRewardPkNf());
                item.setRewardPkRy(x.getRewardPkRy());
                item.setRewardPkSp(x.getRewardPkSp());
                item.setRewardPkYyp(x.getRewardPkYyp());
                item.setRewardPkFn(x.getRewardPkFn());
                item.setRewardPkHj(x.getRewardPkHj());
                item.setDividend(x.getDividend());
                item.setRewardTotal(x.getRewardTotal());
                item.setSalaryTotal(x.getSalaryTotal());
                item.setSalarySchedule(x.getSalarySchedule());
                respList.add(item);
            });
        }

        return respList;
    }

    public DingdingStaffSalaryDetailResp getDetail(DingdingStaffSalaryDetailParamDTO param) {

        DingdingStaffSalary dbData = dingdingStaffSalaryMapper.getDetail(param);
        if(dbData == null){
            return null;
        }

        DingdingStaffSalaryDetailResp result = new DingdingStaffSalaryDetailResp();
        result.setId(dbData.getId());
        result.setBelong(dbData.getBelong());
        result.setCreateUserId(dbData.getCreateUserId());
        result.setCreateUserName(dbData.getCreateUserName());
        result.setCreateTime(dbData.getCreateTime());
        result.setOrgId(dbData.getOrgId());
        result.setOrgCode(dbData.getOrgCode());
        result.setUpdateUserId(dbData.getUpdateUserId());
        result.setUpdateUserName(dbData.getUpdateUserName());
        result.setUpdateTime(dbData.getUpdateTime());
        result.setDel(dbData.getDel());
        result.setUserId(dbData.getUserId());
        result.setPostWage(dbData.getPostWage());
        result.setBasicSalary(dbData.getBasicSalary());
        result.setMonthPerformanceSalaryStandard(dbData.getMonthPerformanceSalaryStandard());
        result.setMonthPerformanceScore(dbData.getMonthPerformanceScore());
        result.setMonthPerformanceCoefficient(dbData.getMonthPerformanceCoefficient());
        result.setMonthPerformanceSalary(dbData.getMonthPerformanceSalary());
        result.setOtherSalary(dbData.getOtherSalary());
        result.setBeforeTaxDeductionTotal(dbData.getBeforeTaxDeductionTotal());
        result.setSalariesPayable(dbData.getSalariesPayable());
        result.setRewardXinghuoJlb(dbData.getRewardXinghuoJlb());
        result.setRewardXinghuoMzc(dbData.getRewardXinghuoMzc());
        result.setRewardXinghuoYl(dbData.getRewardXinghuoYl());
        result.setRewardXinghuoFh(dbData.getRewardXinghuoFh());
        result.setRewardXinghuoHpnk(dbData.getRewardXinghuoHpnk());
        result.setRewardXinghuoBym(dbData.getRewardXinghuoBym());
        result.setRewardXinghuoDz(dbData.getRewardXinghuoDz());
        result.setRewardXinghuoXk(dbData.getRewardXinghuoXk());
        result.setRewardXinghuoFnzt(dbData.getRewardXinghuoFnzt());
        result.setRewardXinghuoYypzt(dbData.getRewardXinghuoYypzt());
        result.setRewardXinghuoXhlyxm(dbData.getRewardXinghuoXhlyxm());
        result.setRewardPkNf(dbData.getRewardPkNf());
        result.setRewardPkRy(dbData.getRewardPkRy());
        result.setRewardPkSp(dbData.getRewardPkSp());
        result.setRewardPkYyp(dbData.getRewardPkYyp());
        result.setRewardPkFn(dbData.getRewardPkFn());
        result.setRewardPkHj(dbData.getRewardPkHj());
        result.setDividend(dbData.getDividend());
        result.setRewardTotal(dbData.getRewardTotal());
        result.setSalaryTotal(dbData.getSalaryTotal());
        result.setSalarySchedule(dbData.getSalarySchedule());
        return result;
    }

    public void updateById(DingdingStaffSalaryUpdateByIdParamDTO param) {
        param.check();

        DingdingStaffSalaryDetailParamDTO queryDetailDTO = new DingdingStaffSalaryDetailParamDTO();
        queryDetailDTO.setId(param.getId());
        DingdingStaffSalary dbData = dingdingStaffSalaryMapper.getDetail(queryDetailDTO);
        if(dbData == null){
            throw new DefaultException("更新的记录不存在");
        }

        dingdingStaffSalaryMapper.updateById(param);
    }

    public void batchUpdateById(List<DingdingStaffSalaryUpdateByIdParamDTO> dataList) {
        if(dataList == null || dataList.size() == 0){
            return;
        }
        dataList.forEach( x -> {
           x.check();
        });

        List<List<DingdingStaffSalaryUpdateByIdParamDTO>> batchList = Lists.partition(dataList, 100);

        for (int i = 0; i < batchList.size(); i++) {
            dingdingStaffSalaryMapper.batchUpdateById(batchList.get(i));
        }
    }

    public void deleteById(DingdingStaffSalaryDeleteByIdParamDTO paramDTO) {
        paramDTO.check();
        dingdingStaffSalaryMapper.deleteById(paramDTO);
    }

    public void add(DingdingStaffSalaryAddParamDTO param) {
        if(param == null){
            return;
        }
        param.setDel(0);
        param.setCreateTime(new Date());
        dingdingStaffSalaryMapper.add(param);
    }

    public void batchAdd(List<DingdingStaffSalaryAddParamDTO> dataList) {
        if(dataList == null || dataList.size() == 0){
            return;
        }

        dataList.forEach(x -> {
            x.setDel(0);
            x.setCreateTime(new Date());
        });

        List<List<DingdingStaffSalaryAddParamDTO>> batchList = Lists.partition(dataList, 100);

        for (int i = 0; i < batchList.size(); i++) {
            dingdingStaffSalaryMapper.batchAdd(batchList.get(i));
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public CommonImportDTO importMonthSalary(MultipartFile multipartFile, String belong, String salarySchedule){

        if(salarySchedule == null || "".equals(salarySchedule)){
            throw new DefaultException("月份不能为空");
        }
        if(belong == null || "".equals(belong)){
            throw new DefaultException("归属标识不能为空");
        }

        IMPORT_TOTAL_COUNT = 0;
        IMPORT_SUCCESS_COUNT = 0;
        IMPORT_EXECUTE_COUNT = 0;

        File file = multipartFileToFile(multipartFile);
        dingdingStaffSalaryMapper.deleteByBelongAndSalarySchedule(belong, salarySchedule);
        readAndHandler(file, belong, salarySchedule);

        return new CommonImportDTO(IMPORT_TOTAL_COUNT, IMPORT_SUCCESS_COUNT, IMPORT_TOTAL_COUNT - IMPORT_SUCCESS_COUNT);
    }

    private File multipartFileToFile(MultipartFile multipartFile){
        File file = new File(multipartFile. getOriginalFilename());
        try {
            InputStream inputStream = multipartFile.getInputStream();
            OutputStream outputStream = new FileOutputStream(file);
            byte[] buffer = new byte[1024];
            int length;
            while ((length = inputStream. read(buffer)) != -1) {
                outputStream.write(buffer, 0, length);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        if(file == null){
            throw new DefaultException("文件不合法");
        }
        return file;
    }

    private Object readAndHandler(File file, String belong, String salarySchedule) {


        EasyExcel.read(file, DingdingStaffSalaryImportDataDTO.class, new ReadListener<DingdingStaffSalaryImportDataDTO>() {

            public static final int BATCH_COUNT = 100;
            private List<DingdingStaffSalaryImportDataDTO> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

            @Override
            public void invoke(DingdingStaffSalaryImportDataDTO data, AnalysisContext context) {

                IMPORT_TOTAL_COUNT++;

                if(data.getStaffCode() == null || "".equals(data.getStaffCode().trim())){
                    return;
                }
                cachedDataList.add(data);

                if (cachedDataList.size() >= BATCH_COUNT) {
                    execute();
                    cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                if(cachedDataList != null && cachedDataList.size() > 0){
                    execute();
                }
                logger.info("所有数据解析完成！");
            }

            /**
             * 批次数据处理
             */
            private void execute() {

                IMPORT_EXECUTE_COUNT++;
                logger.info("正在执行第{}次数据导入...", IMPORT_EXECUTE_COUNT);

                if(cachedDataList != null && cachedDataList.size() > 0){
                    List<DingdingStaffSalaryAddParamDTO> dataList = new ArrayList<>();
                    for (int i = 0; i < cachedDataList.size(); i++) {
                        DingdingStaffSalaryImportDataDTO data = cachedDataList.get(i);
                        String userId = valuePreHandler(data.getStaffCode());
                        String postWage = valuePreHandler(data.getPostWage());
                        String title = data.getTitle();
                        String basicSalary = valuePreHandler(data.getBasicSalary());
                        String performanceSalaryStandard = valuePreHandler(data.getMonthPerformanceSalaryStandard());
                        String performanceScore = valuePreHandler(data.getMonthPerformanceScore());
                        String performanceCoefficient = valuePreHandler(data.getMonthPerformanceCoefficient());
                        String performanceSalary = valuePreHandler(data.getMonthPerformanceSalary());
                        String otherSalary = valuePreHandler(data.getOtherSalary());
                        String beforeTaxDeductionTotal = valuePreHandler(data.getBeforeTaxDeductionTotal());
                        String rewardXinghuoJlb = valuePreHandler(data.getRewardXinghuoJlb());
                        String rewardXinghuoMzc = valuePreHandler(data.getRewardXinghuoMzc());
                        String rewardXinghuoYl = valuePreHandler(data.getRewardXinghuoYl());
                        String rewardXinghuoFh = valuePreHandler(data.getRewardXinghuoFh());
                        String rewardXinghuoHpnk = valuePreHandler(data.getRewardXinghuoHpnk());
                        String rewardXinghuoBym = valuePreHandler(data.getRewardXinghuoBym());
                        String rewardXinghuoDz = valuePreHandler(data.getRewardXinghuoDz());
                        String rewardXinghuoXk = valuePreHandler(data.getRewardXinghuoXk());
                        String rewardXinghuoFnzt = valuePreHandler(data.getRewardXinghuoFnzt());
                        String rewardXinghuoYypzt = valuePreHandler(data.getRewardXinghuoYypzt());
                        String rewardPkNf = valuePreHandler(data.getRewardPkNf());
                        String rewardPkRy = valuePreHandler(data.getRewardPkRy());
                        String rewardPkSp = valuePreHandler(data.getRewardPkSp());
                        String rewardPkYyp = valuePreHandler(data.getRewardPkYyp());
                        String rewardPkFn = valuePreHandler(data.getRewardPkFn());
                        String rewardPkWjcc = valuePreHandler(data.getRewardPkWjcc());
                        String dividend = valuePreHandler(data.getDividend());
                        String rewardDeduction = valuePreHandler(data.getRewardDeduction());

                        DingdingStaffSalaryAddParamDTO itemDTO = new DingdingStaffSalaryAddParamDTO();
                        itemDTO.setBelong(belong);
                        itemDTO.setUserId(userId);
                        itemDTO.setTitle(title);
                        itemDTO.setPostWage(postWage);
                        itemDTO.setBasicSalary(basicSalary);
                        itemDTO.setMonthPerformanceSalaryStandard(performanceSalaryStandard);
                        itemDTO.setMonthPerformanceScore(performanceScore);
                        itemDTO.setMonthPerformanceCoefficient(performanceCoefficient);
                        itemDTO.setMonthPerformanceSalary(performanceSalary);
                        itemDTO.setOtherSalary(otherSalary);
                        itemDTO.setBeforeTaxDeductionTotal(beforeTaxDeductionTotal);
                        itemDTO.setSalariesPayable(calculateSalariesPayable(basicSalary, performanceSalary, otherSalary, beforeTaxDeductionTotal));
                        itemDTO.setRewardXinghuoJlb(rewardXinghuoJlb);
                        itemDTO.setRewardXinghuoMzc(rewardXinghuoMzc);
                        itemDTO.setRewardXinghuoYl(rewardXinghuoYl);
                        itemDTO.setRewardXinghuoFh(rewardXinghuoFh);
                        itemDTO.setRewardXinghuoHpnk(rewardXinghuoHpnk);
                        itemDTO.setRewardXinghuoBym(rewardXinghuoBym);
                        itemDTO.setRewardXinghuoDz(rewardXinghuoDz);
                        itemDTO.setRewardXinghuoXk(rewardXinghuoXk);
                        itemDTO.setRewardXinghuoFnzt(rewardXinghuoFnzt);
                        itemDTO.setRewardXinghuoYypzt(rewardXinghuoYypzt);
                        itemDTO.setRewardXinghuoXhlyxm(calculateTotal(rewardXinghuoJlb, rewardXinghuoMzc, rewardXinghuoYl, rewardXinghuoFh, rewardXinghuoHpnk, rewardXinghuoBym,
                                rewardXinghuoDz, rewardXinghuoXk, rewardXinghuoFnzt, rewardXinghuoYypzt));
                        itemDTO.setRewardPkNf(rewardPkNf);
                        itemDTO.setRewardPkRy(rewardPkRy);
                        itemDTO.setRewardPkSp(rewardPkSp);
                        itemDTO.setRewardPkYyp(rewardPkYyp);
                        itemDTO.setRewardPkFn(rewardPkFn);
                        itemDTO.setRewardPkWjcc(rewardPkWjcc);
                        itemDTO.setRewardPkHj(calculateTotal(rewardPkNf, rewardPkRy, rewardPkSp, rewardPkYyp, rewardPkFn, rewardPkWjcc));
                        itemDTO.setDividend(dividend);
                        itemDTO.setRewardDeduction(rewardDeduction);
                        String calculateRewardDeduction;
                        if(rewardDeduction == null || "".equals(rewardDeduction)){
                            calculateRewardDeduction = rewardDeduction;
                        } else {
                            if(rewardDeduction.contains("-")){
                                calculateRewardDeduction = rewardDeduction.replace("-", "");
                            } else {
                                calculateRewardDeduction = "-" + rewardDeduction;
                            }
                        }
                        itemDTO.setRewardTotal(calculateTotal(itemDTO.getRewardXinghuoXhlyxm(), itemDTO.getRewardPkHj(), itemDTO.getDividend(), calculateRewardDeduction));
                        itemDTO.setSalaryTotal(calculateTotal(itemDTO.getSalariesPayable(), itemDTO.getRewardTotal()));
                        itemDTO.setSalarySchedule(salarySchedule);
                        dataList.add(itemDTO);
                        IMPORT_SUCCESS_COUNT++;
                    }
                    if(dataList.size() > 0){
                        batchAdd(dataList);
                    }
                }
            }
        }).sheet().headRowNumber(3).doRead();

        return null;
    }

    private String valuePreHandler(String cellValue){

        if(cellValue == null || "".equals(cellValue)){
            return cellValue;
        }

        cellValue = cellValue.trim();
        cellValue = cellValue.replaceAll(",", "");
        cellValue = cellValue.replaceAll("\\(", "");
        cellValue = cellValue.replaceAll("\\)", "");

        return cellValue;
    }

    private String calculateSalariesPayable(String basicSalary, String performanceSalary, String otherSalary, String beforeTaxDeductionTotal){
        double result = 0;
        try{
            if(basicSalary != null && !"".equals(basicSalary)){
                result += Double.parseDouble(basicSalary.trim());
            }
            if(performanceSalary != null && !"".equals(performanceSalary)){
                result += Double.parseDouble(performanceSalary.trim());
            }
            if(otherSalary != null && !"".equals(otherSalary)){
                result += Double.parseDouble(otherSalary.trim());
            }
            if(beforeTaxDeductionTotal != null && !"".equals(beforeTaxDeductionTotal)){
                result -= Double.parseDouble(beforeTaxDeductionTotal.trim());
            }
        } catch (Exception e){
            logger.error("存在异常的工资数据", e);
            throw new DefaultException("工资数据异常");
        }

        BigDecimal bg = new BigDecimal(result).setScale(4, RoundingMode.UP);

        return String.valueOf(bg.doubleValue());
    }

    private String calculateTotal(String... args){
        double result = 0;

        if(args == null || args.length == 0){
            return "0";
        }

        try{
            for (int i = 0; i < args.length; i++) {
                if(args[i] != null && !"".equals(args[i])){
                    result += Double.parseDouble(args[i].trim());
                }
            }
        } catch (Exception e){
            logger.error("存在异常的工资数据", e);
            throw new DefaultException("工资数据异常");
        }

        BigDecimal bg = new BigDecimal(result).setScale(2, RoundingMode.UP);

        return String.valueOf(bg.doubleValue());
    }
}
